package com.google.code.swingonfire.image;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;

public abstract class AbstractImageProvider implements ImageProvider
{

    private final boolean transparent;
    private final double updateAfter;

    private Image image;
    private Dimension size;
    private long accessTimestamp = System.currentTimeMillis();
    private boolean updateNeeded = false;

    /**
     * Creates a new image provider. The image will never update itself automatically.
     * 
     * @param transparent true if transparent
     */
    public AbstractImageProvider(boolean transparent)
    {
        this(transparent, Double.NaN);
    }

    /**
     * Creates a new image provider.
     * 
     * @param transparent true if transparent
     * @param updateAfter a timespan in seconds - the image will be automatically updated, if it hasn't been access for
     *            the specified time. Specify updateAfter <= 0 to update the image each time it is accessed, specify
     *            updateAfter = Double.NaN to disable auto update
     */
    public AbstractImageProvider(boolean transparent, double updateAfter)
    {
        super();

        this.transparent = transparent;
        this.updateAfter = updateAfter;
    }

    /**
     * Returns true if the image should be transparent
     * 
     * @return true if transparent
     */
    public boolean isTransparent()
    {
        return transparent;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Image getImage()
    {
        if (!Double.isNaN(updateAfter))
        {
            updateNeeded = (System.currentTimeMillis() - accessTimestamp) > (long) (updateAfter * 1000);
        }

        accessTimestamp = System.currentTimeMillis();

        Dimension preferredSize = getPreferredSize();

        if ((preferredSize == null) || (preferredSize.width < 1) || (preferredSize.height < 1))
        {
            image = null;
            size = null;
            updateNeeded = false;

            return null;
        }

        if ((image == null) || (!preferredSize.equals(size)))
        {
            Image target = createImage(preferredSize.width, preferredSize.height);

            updateImage(target, preferredSize.width, preferredSize.height);

            image = target;
            size = (Dimension) preferredSize.clone();
            updateNeeded = false;

            return image;
        }

        if (isUpdateNeeded())
        {
            updateImage(image, size.width, size.height);
            updateNeeded = false;
        }

        return image;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Graphics2D getGraphics()
    {
        Image image = getImage();

        return (image != null) ? createGraphics(image) : null;
    }

    /**
     * Returns the preferred size of the image
     * 
     * @return the dimension, may be null or < 1
     */
    protected abstract Dimension getPreferredSize();

    protected Image createImage(int width, int height)
    {
        System.out.println(width + ", " + height);
        return new BufferedImage(width, height, (isTransparent()) ? BufferedImage.TYPE_INT_ARGB
            : BufferedImage.TYPE_INT_RGB);
    }

    protected void updateImage(Image image, int widht, int height)
    {
        drawImage(createGraphics(image), widht, height);
    }

    protected Graphics2D createGraphics(Image image)
    {
        return (Graphics2D) image.getGraphics();
    }

    protected abstract void drawImage(Graphics2D g, int width, int height);

    /**
     * {@inheritDoc}
     */
    @Override
    public int getWidth()
    {
        Image image = getImage();

        return (image != null) ? image.getWidth(null) : 0;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int getHeight()
    {
        Image image = getImage();

        return (image != null) ? image.getHeight(null) : 0;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isUpdateNeeded()
    {
        return updateNeeded;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updateNeeded()
    {
        updateNeeded = true;
    }

}
